<template>
  <demo-content :title="title">
    <h2>事件传播模式</h2>
    在 js 中有两种事件传播模式：
    <ol>
      <li>
        捕获模式(capturing):
        当事件发生时，事件首先被最外层元素接收到，然后依次向内层传播。
      </li>
      <li>
        冒泡模式(bubbling):当事件发生时，该事件首先被最内层元素接受到，然后依次向外层元素传播。
      </li>
    </ol>
    这两种模式决定了 html
    中元素接收到事件的顺序，接收到事件的顺序不同，自然事件监听函数被触发的顺序也就不同。
    通过 addEventListener 第三个参数控制传播模式，false 为冒泡模式，true
    为捕获模式，默认为冒泡模式(false)。点击 border 也算点击到对象。 vue @click
    默认的绑定冒泡模式，@click.capture 绑定捕获模式。<br />
    <strong style="color: red"
      >通过 event.stopPropagation() 可以阻断下面的传播链。event.preventDefault()
      会阻止元素的默认行为，如 a
      标签的跳转、右键弹出上下文菜单，在捕获模式或冒泡模式阻止默认行为都可以。</strong
    >
    <br />
    <img src="/imgs/event-stream.png" alt="event-stream" srcset="" />
    <div>
      <template v-for="prefix of ['t1', 't2']">
        <div
          class="outer"
          :id="`${prefix}-outer`"
          style="display: inline-block"
        >
          {{ prefix === "t1" ? "捕获模式" : "冒泡模式" }} {{ prefix }}-outer
          <div class="middle" :id="`${prefix}-middle`">
            {{ prefix }}.middle
            <div class="inner" :id="`${prefix}-inner`">{{ prefix }}-inner</div>
          </div>
        </div>
      </template>
      <div
        class="outer"
        id="t3-outer"
        style="display: inline-block"
        @click="console.log('t3-outer bubble')"
        @click.capture="console.log('t3-outer capture')"
      >
        vue 事件模式
        <div
          class="middle"
          :id="`t3-middle`"
          @click="
            (e) => {
              console.log('t3-middle bubble');
              // t3-outer bubble 将不被打印，因为事件链被阻断了
              e.stopPropagation();
            }
          "
          @click.capture="console.log('t3-middle capture')"
        >
          t3.middle

          <a
            style="display: block; background-color: red"
            :id="`t3-inner`"
            href="https://www.baidu.com"
            @click="console.log('t3-inner bubble')"
            @click.capture="
              (e) => {
                console.log('t3-inner capture');
                e.preventDefault();
              }
            "
          >
            t3-inner
          </a>
        </div>
      </div>
    </div>
    <p>
      如下面的例子，div 下有一个 a，通过在 div 的 click.capture
      中阻断事件传播，并不能阻止点击 a 跳转页面，虽然此时 a 已经不能收到事件了。
    </p>
    <div
      style="
        width: 300px;
        height: 100px;
        padding: 20px;
        background-color: chartreuse;
      "
      @click.capture="
        (e) => {
          e.stopPropagation();
          console.log('parent div event');
        }
      "
    >
      <a
        style="
          background-color: pink;
          height: 50px;
          display: block;
          margin: 10px;
        "
        href="https://www.baidu.com"
        @click="console.log('sub a click bubble')"
        @click.capture="console.log('sub a click capture')"
        >click me</a
      >
    </div>
  </demo-content>
</template>

<script setup lang="ts">
import { onMounted } from "vue";
const title = "事件模型";

// 指定组件上的属性
defineOptions({ title });
const getById = (id: string) => document.getElementById(id)!;
function bindEvent(id: string, bubbleMode?: boolean) {
  getById(id).addEventListener(
    "click",
    () => {
      console.log(id);
    },
    bubbleMode
  );
}

const names = ["outer", "middle", "inner"];
onMounted(() => {
  // 捕获模式测试
  names.forEach((n) => bindEvent(`t1-${n}`, true));
  // 冒泡模式
  names.forEach((n) => bindEvent(`t2-${n}`, false));
});
</script>

<style scoped>
.outer {
  width: 300px;
  margin: 20px;
  border: 20px solid green;
  padding: 20px;
  background-color: greenyellow;
}
.middle {
  margin: 20px;
  border: 20px solid blue;
  padding: 20px;
  background-color: deepskyblue;
}
.inner {
  width: 70px;
  height: 50px;
  margin: 20px;
  border: 20px solid red;
  padding: 20px;
  background-color: pink;
}
</style>
